package com.github.baichuan.application.scaffold.security;


import com.github.baichuan.application.scaffold.domain.identity.ApplicationMember;
import com.github.baichuan.application.scaffold.domain.identity.ApplicationMemberService;
import com.github.baichuan.application.scaffold.domain.identity.Member;
import com.github.baichuan.application.scaffold.domain.identity.MemberLogin;
import com.github.baichuan.application.scaffold.domain.organization.Org;
import com.github.baichuan.application.scaffold.dto.authentication.DingCredential;
import com.github.baichuan.application.scaffold.dto.authentication.PasswordCredential;
import com.github.baichuan.application.scaffold.dto.authentication.UserInfo;
import com.github.baichuan.application.scaffold.jpa.identity.ApplicationMemberRepository;
import com.github.baichuan.application.scaffold.jpa.identity.MemberLoginRepository;
import com.github.baichuan.application.scaffold.jpa.identity.MemberRepository;
import com.github.baichuan.web.dto.Result;
import com.github.baichuan.application.scaffold.domain.application.Application;
import com.github.baichuan.application.scaffold.domain.application.ApplicationService;
import com.github.baichuan.web.security.PasswordEncryption;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.time.LocalDate;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.concurrent.ConcurrentHashMap;
import java.util.stream.Collectors;

/**
 * 用户认证服务
 */
@Service
@Transactional(rollbackFor = Exception.class)
public class AuthenticationService {
    @Autowired
    private ApplicationService applicationService;
    @Autowired
    private MemberRepository memberRepository;
    @Autowired
    private ApplicationMemberRepository applicationMemberRepository;
    @Autowired
    private ApplicationMemberService memberService;
    @Autowired
    private MemberLoginRepository loginRepository;
    @Autowired
    private PasswordEncryption passwordEncryption;
    @Autowired
    private PasswordSecurityProvider passwordSecurityProvider;


    /**
     * 用户名密码验证
     * @param applicationCode
     * @param credential
     * @return
     */
    public Result auth(String applicationCode, PasswordCredential credential) {
        Application application = applicationService.findByCode(applicationCode);
        String loginName = credential.getLoginName().trim();

        PasswordSecurityPolicy passwordSecurity = passwordSecurityProvider.get(application);
        Result attackDetect = passwordSecurity.attackDetect(loginName);
        if(!attackDetect.isSuccess()){
            return attackDetect;
        }

        Optional<Member> optionalMember = memberRepository.find(application.getRealm(), credential.getLoginName().trim());
        if (!optionalMember.isPresent()) {
//            return Result.fail("用户名或密码错误");
            return passwordSecurity.fail(loginName);
        }


        Member member = optionalMember.get();
        String cipherPassword = memberService.encrypt(credential.getPassword().trim(), member.getSalt());
        if (!cipherPassword.equals(member.getPassword())) {
//            return Result.fail("用户名或密码错误");
            return passwordSecurity.fail(loginName);
        }

        Optional<ApplicationMember> optionalApplicationMember = applicationMemberRepository.find(application, member);
        if (!optionalApplicationMember.isPresent()) {
            return Result.fail("没有应用访问权限");
        }

        ApplicationMember applicationMember = optionalApplicationMember.get();
        if (applicationMember.getExpire().isBefore(LocalDate.now())) {
            return Result.fail("账号已过期");
        }

        UserInfo userInfo = userInfo(applicationMember);
        passwordSecurity.success(loginName);
        log(applicationMember, credential.getIp());

        Result passwordExpire = passwordSecurity.overdue(member.getPasswordUpdateDate());
        if(passwordExpire.isSuccess()){
            userInfo.setPasswordExpire(true);
        }

        return Result.success("登陆成功", userInfo);
    }

    /**
     * 钉钉用户验证
     * @param application
     * @param credential
     * @return
     */
    public Result auth(String application, DingCredential credential){
        Optional<ApplicationMember> optional = memberService.findByDingUser(application, credential.getUnionId());
        if(optional.isPresent()){
            ApplicationMember member = optional.get();
            if (member.getExpire().isBefore(LocalDate.now())) {
                return Result.fail("账号已过期");
            }
            Result result = Result.success("登陆成功", userInfo(member));
            return result;
        } else {
            return Result.fail("钉钉用户未绑定账号");
        }
    }

    private UserInfo userInfo(ApplicationMember member){
        Org org = member.getOrg();
        List<String> codes = memberService.getAuthorizationCodes(member);
        UserInfo data = UserInfo.builder()
                .memberUid(member.getId())
                .loginName(member.getMember().getLoginName())
                .name(member.getMember().getName())
                .memberType(member.getMemberType())
                .orgUid(null != org ? org.getUid() : null)
                .orgName(null != org ? org.getName() : null)
                .orgBranch(null != org ? org.getBranch() : null)
                .authorizations(StringUtils.join(codes, ','))
                .roles(member.getRoles().stream().map(x -> String.valueOf(x.getId())).collect(Collectors.joining(",")))
                .build();
        return data;
    }

    private void log(ApplicationMember applicationMember, String ip) {
        applicationMember.setLoginCount(applicationMember.getLoginCount() + 1);
        MemberLogin login = MemberLogin.newInstance(applicationMember.getMember(), applicationMember.getApplication(), ip);
        loginRepository.save(login);
    }
}
